<?php

/*
    Dill is a tool for creating websites with PHP.
    Copyright (C) 2014  Jannik Haberbosch

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// Require required classes.

class WebsiteProject
{
	private static $website_project = NULL;
	
	private $js_elements = array();
	private $css_elements = array();
	private $template_elements = array();
	private $module_elements = array();
	private $website_structure = array();
	private $project_name;
	private $db;
	private $ssq;
	private $abspath_website_project;
	public $abspath_media;
	public $abspath_downloads;
	public $managing_media_downloads = NULL;

	private $editing_database_element = FALSE;
	private $container_being_edited_id = NULL;
	private $page_being_edited_id = NULL;
	private $editing_file = FALSE;
	private $editing_file_type = NULL;	
	private $editing_file_name = NULL;

	private $all_pages = array();
	private $website_project_pages = array();

	// Testing.
	public $managing_client_data = array();


	public function get_project_name()
	{
		return $this->project_name;
	}


	public function close()
	{
		$this->db->close();
		WebsiteProject::$website_project = NULL;
	}


	public function status_managing_media_downloads( $managing_what )
	{
		$this->managing_media_downloads = $managing_what;
	}


	public function get_abspath_website_project()
	{
		return $this->abspath_website_project;
	}


	public function get_abspath_media_or_downloads()
	{
		if( $this->managing_media_downloads == MANAGING_MEDIA )
		{
			return $this->abspath_media;
		}
		else
		{
			return $this->abspath_downloads;
		}
	}


	public function create_page_and_containers( $pagename, $selected_template,
		$sort_id, $parent_id = NULL )
	{
		// Get page containersfor the selected template:
		$page_containers = $this->template_elements[$selected_template]->get( "page_container_names" );
		
		if( $parent_id == NULL )
		{
			$this->ssq->insert(
				"page",
				array(
					"template",
					"name",
					"sort_id"
				),
				array(
					$selected_template,
					$pagename,
					$sort_id
				)
			);
		}
		else
		{
			$this->ssq->insert(
				"page",
				array(
					"template",
					"name",
					"sort_id",
					"parent_id"
				),
				array(
					$selected_template,
					$pagename,
					$sort_id,
					$parent_id
				)
			);
		}

		// Create containers for the page.
		$insert_id = $this->ssq->get_insertid();
		foreach( $page_containers as $container )
		{
			$this->ssq->insert(
				"container",
				array( "name", "content", "page_id" ),
				array( $container, "", $insert_id )
			);
		}
	}


	public function get_website_structure()
	{
		return $this->website_structure;
	}


	public function editor_editing_nothing()
	{
		$this->editing_file = FALSE;
		$this->editing_database_element = FALSE;
		$this->container_being_edited_id = NULL;
		$this->page_being_edited_id = NULL;
		$this->editing_file_name = NULL;
		$this->editing_file_type = NULL;
	}


	public function save_editor_content( $data )
	{
		if( $this->editing_file )
		{
			switch( $this->editing_file_type )
			{
				case WEBSITE_PROJECT_JAVASCRIPT_FILE:
				{
					return $this->js_elements[$this->editing_file_name]->set_content( $data );
				}
				case WEBSITE_PROJECT_CSS_FILE:
				{
					return $this->css_elements[$this->editing_file_name]->set_content( $data );
				}
				case WEBSITE_PROJECT_TEMPLATE_FILE:
				{
					$result = $this->template_elements[$this->editing_file_name]->set_content( $data );
					$this->template_elements[$this->editing_file_name]->update_container_names();
					// Update (delete and create) containers for all pages that use the edited template.
					foreach( $this->website_project_pages as $wpp )
					{
						if( strcmp( $wpp->get( "template" ), $this->editing_file_name ) == 0 )
						{
							$this->page_delete_create_containers( $wpp->get( "id" ), $this->editing_file_name );
						}
					}
					$this->recreate_website_structure();
					return $result;
				}
				case WEBSITE_PROJECT_MODULE_FILE:
				{
					return $this->module_elements[$this->editing_file_name]->set_content( $data );
				}
			}
		}
		else if( $this->editing_database_element )
		{
			// Write the "data" to the selected container.
			$this->ssq->update(
				"container",
				array(
					array( "content", "=", $data )
				),
				array(
					array( "id", "=", $this->container_being_edited_id ),
					array( "AND" ),
					array( "page_id", "=", $this->page_being_edited_id )
				)
			);
			$this->recreate_website_structure();
		}
	}


	public function update_file_editing_info( $file_type, $file_name )
	{
		$this->editing_file = TRUE;
		$this->editing_file_type = $file_type;
		$this->editing_file_name = $file_name;

		$this->editing_database_element = FALSE;
		$this->container_being_edited_id = NULL;
		$this->page_being_edited_id = NULL;
	}


	public function update_database_editing_info( $container_id, $page_id )
	{
		$this->editing_database_element = TRUE;
		$this->container_being_edited_id = $container_id;
		$this->page_being_edited_id = $page_id;

		$this->editing_file = FALSE;
		$this->editing_file_type = NULL;
		$this->editing_file_name = NULL;
	}


	public function add_element( $element )
	{
		switch( get_class( $element ) )
		{
			case "WebsiteProjectCSSFile":
			{
				$this->css_elements[$element->get_name()] = $element;
				break;
			}
			case "WebsiteProjectJSFile":
			{
				$this->js_elements[$element->get_name()] = $element;
				break;
			}
			case "WebsiteProjectTemplate":
			{
				$this->template_elements[$element->get_name()] = $element;
				break;
			}
			case "WebsiteProjectModule":
			{
				$this->module_elements[$element->get_name()] = $element;
				break;
			}			
		}
	}

	
	public function rename_element()
	{
	}

	
	public function delete_element()
	{
	}



	public function rename_file_routine( $userinput, $selected_element_name, $filetype )
	{
		/*
		*/
		switch( $filetype )
		{
			case WEBSITE_PROJECT_CSS_FILE:
			{
				$arr = &$this->css_elements;
				break;
			}
			case WEBSITE_PROJECT_JAVASCRIPT_FILE:
			{
				$arr = &$this->js_elements;
				break;
			}
			case WEBSITE_PROJECT_TEMPLATE_FILE:
			{
				$arr = &$this->template_elements;
				break;
			}
			case WEBSITE_PROJECT_MODULE_FILE:
			{
				$arr = &$this->module_elements;
				break;
			}
		}
		try
		{
			$arr[$selected_element_name]->rename( $userinput );
			$arr[$userinput] = $arr[$selected_element_name];
			unset( $arr[$selected_element_name] );
			asort( $arr );
		}
		catch( Exception $e )
		{
			throw $e;
		}
	}


	public function delete_file()
	{
	}


	public function get_elements( $element_type )
	{
		switch( $element_type )
		{
			case WEBSITE_PROJECT_CSS_FILES: return $this->css_elements;
			case WEBSITE_PROJECT_JAVASCRIPT_FILES: return $this->js_elements;
			case WEBSITE_PROJECT_MODULE_FILES: return $this->module_elements;
			case WEBSITE_PROJECT_TEMPLATE_FILES: return $this->template_elements;
		}
	}


	public function set_elements( $elements, $type )
	{
		switch( $type )
		{
			case WEBSITE_PROJECT_CSS_FILES:
			{
				$this->css_elements = $elements;
				break;
			}
			case WEBSITE_PROJECT_MODULE_FILES:
			{
				$this->module_elements = $elements;
				break;
			}
			case WEBSITE_PROJECT_TEMPLATE_FILES:
			{
				$this->template_elements = $elements;
				break;
			}
			case WEBSITE_PROJECT_JAVASCRIPT_FILES:
			{
				$this->js_elements = $elements;
				break;
			}
		}
	}
	
	
	private function __construct( $project_name )
	{
		$this->project_name = $project_name;
		$this->abspath_website_project = makepath( ABSPATH_WEBSITE_PROJECTS,
			$this->project_name
		);

		$this->abspath_media = makepath(
			$this->abspath_website_project,
			RELPATH_WEBSITE_MEDIA
		);

		$this->abspath_downloads = makepath(
			$this->abspath_website_project,
			RELPATH_WEBSITE_DOWNLOADS
		);
		

		WebsiteProjectModule::set_abspath_directory( $this->abspath_website_project );
		WebsiteProjectTemplate::set_abspath_directory( $this->abspath_website_project );
		WebsiteProjectJSFile::set_abspath_directory( $this->abspath_website_project );
		WebsiteProjectCSSFile::set_abspath_directory( $this->abspath_website_project );
				

		// Create the database connection.
		try
		{
			$this->db = new SQLite3(
				makepath(
					ABSPATH_WEBSITE_PROJECTS,
					$this->project_name,
					RELPATH_DATABASE,
					DB_NAME
				)
			);
		}
		catch( Exception $e )
		{
			throw $e;
		}
		// Try to enable foreign key support for the SQLite3 database.
		$res = $this->db->exec(
			"PRAGMA foreign_keys = ON;"
		);
		if( $res === FALSE )
		{
			throw new Exception( ERROR_DB_PRAGMA_FOREIGN_KEYS_SUPPORT );
		}

		// Use the SimpleSQLiteQuery to simplify CRUD operations.
		$this->ssq = new SimpleSQLiteQuery( $this->db );

		/* In any case???, preload modules, templates, js files, css files, pages
		and containers. */
		$this->load_template_elements();
		$this->load_module_elements();
		$this->load_js_elements();		
		$this->load_css_elements();

		$this->fetch_all_pages();
		$this->create_all_pages();

		$this->website_structure = $this->create_entire_website_structure();
	}


	private function create_entire_website_structure( $parent_id = NULL )
	{
		$branch_pages = array();
		foreach( $this->website_project_pages as $value )
		{
			if( $value->get( "parent_id" ) == $parent_id )
			{
				$branch_pages[] = $value;			
			}
		}

		
		$arr = array();
		foreach( $branch_pages as $row )
		{
			// Create the page with its containers.
			$arr[$row->get( "name" )] = array(
				"self" => NULL,
				"children" => NULL
			);
			$arr[$row->get( "name" )]["self"] = $row;

			// Fetch and create children of this page.
			try
			{
				$arr[$row->get( "name" )]["children"] = $this->create_entire_website_structure( $row->get( "id" ) );		
			}
			catch( Exception $e )
			{
				throw $e;
			}
		}
		
		return $arr;
	}


	private function fetch_pages( $parent_id = NULL )
	{
		if( $parent_id == NULL )
		{
			try
			{
				$rows = $this->ssq->select(
					"*",
					array( "page" ),
					array(
						array( "parent_id", "IS", NULL )
					),
					"ORDER BY sort_id"
				);
			}
			catch( Exception $e )
			{
				throw $e;
			}		
		}
		else
		{
			try
			{
				$rows = $this->ssq->select(
					"*",
					array( "page" ),
					array(
						array( "parent_id", "=", $parent_id )
					),
					"ORDER BY sort_id"
				);
			}
			catch( Exception $e )
			{
				throw $e;
			}		
		}
		return $rows;
	}


	private function fetch_containers( $page_id )
	{
		try
		{
			$rows = $this->ssq->select(
				"*",
				array( "container" ),
				array(
					array( "page_id", "=", $page_id )
				)
			);
		}
		catch( Exception $e )
		{
			throw $e;
		}
		return $rows;
	}


	private function load_template_elements()
	{
		$files = get_filenames( WebsiteProjectTemplate::get_abspath_directory() );
		foreach( $files as $f )
		{
			$this->template_elements[$f] = new WebsiteProjectTemplate( $f );
		}
	}


	private function load_module_elements()
	{
		$files = get_filenames( WebsiteProjectModule::get_abspath_directory() );
		foreach( $files as $f )
		{
			$this->module_elements[$f] = new WebsiteProjectModule( $f );
		}
	}


	private function load_js_elements()
	{
		$files = get_filenames( WebsiteProjectJSFile::get_abspath_directory() );
		foreach( $files as $f )
		{
			$this->js_elements[$f] = new WebsiteProjectJSFile( $f );
		}
	}


	private function load_css_elements()
	{
		$files = get_filenames( WebsiteProjectCSSFile::get_abspath_directory() );
		foreach( $files as $f )
		{
			$this->css_elements[$f] = new WebsiteProjectCSSFile( $f );
		}
	}
	
	

	public static function create( $project_name )
	{
		/* On success, creates a new website project directory and returns an
		instance of itself.
		On failure, an exception is thrown.

		Parameters		$project_name
						string
						The name of the project to set up.

		Return values 	WebsiteProject object or Void (in which case an exception
						is thrown).

		*/
		try
		{
			self::setup_website_project( $project_name );
			self::$website_project = new WebsiteProject( $project_name );			
		}
		catch( Exception $e )
		{
			throw $e;
		}
	
		return self::$website_project;
	}


	public static function load( $project_name )
	{
		/* On success, validates and loads an existing website project directory
		and returns an instance of itself.
		On failure an exception is thrown.

		Parameters		$project_name
						string
						The name of the project to validate and load.

		Return values	WebsiteProject object or Void (in which case an exception
						is thrown).

		*/
		$error_msg = NULL;
		try
		{
			self::validate_website_project( $project_name, $error_msg );
			self::$website_project = new WebsiteProject( $project_name );
		}
		catch( Exception $e )
		{
			throw $e;
		}

		
		return self::$website_project;
	}


	// Static methods.
	private static function setup_website_project( $project_name )
	{
		/* This function sets up a new website project.  On failure, an exception
		is thrown.  On success, TRUE is returned.

		Parameters		$project_name
						string
						The name of the project.

		Return values	TRUE or throws an exception.
						TRUE is returned on success; an exception is thrown on
						failure.

		Read through the body of this function in order to know how it works.

		*/

		// The path to the root directory of the website project.
		$project_root = makepath( ABSPATH_WEBSITE_PROJECTS, $project_name );

		// Try to create the website project directory.
		if( !mkdir( $project_root, 0777, TRUE ) )
		{
			// The website project could not be created.
			throw new Exception(
				sprintf(
					ERROR_CREATE_DIRECTORY,
					$project_name
				)
			);
		}

		// Redefine constants during runtime if necessary.
		if( defined( "RELPATH_WEBSITE" ) )
		{
			runkit_constant_redefine( "RELPATH_WEBSITE", implode( DIRECTORY_SEPARATOR, array( $project_name ) ) );
		}
		else
		{
			define( "RELPATH_WEBSITE", implode( DIRECTORY_SEPARATOR, array( $project_name ) ) );		
		}
		
		if( defined( "RELPATH_WEBSITE_CSS" ) )
		{
			runkit_constant_redefine( "RELPATH_WEBSITE_CSS", implode( DIRECTORY_SEPARATOR, array( $project_name, "css" ) ) );		
		}
		else
		{
			define( "RELPATH_WEBSITE_CSS", implode( DIRECTORY_SEPARATOR, array( $project_name, "css" ) ) );		
		}
		
		if( defined( "RELPATH_WEBSITE_DOWNLOADS" ) )
		{
			runkit_constant_redefine( "RELPATH_WEBSITE_DOWNLOADS", implode( DIRECTORY_SEPARATOR, array( $project_name, "downloads" ) ) );
		}
		else
		{
			define( "RELPATH_WEBSITE_DOWNLOADS", implode( DIRECTORY_SEPARATOR, array( $project_name, "downloads" )) );		
		}
		
		if( defined( "RELPATH_WEBSITE_JS" ) )
		{
			runkit_constant_redefine( "RELPATH_WEBSITE_JS", implode( DIRECTORY_SEPARATOR, array( $project_name, "js" ) ) );
		}
		else
		{
			define( "RELPATH_WEBSITE_JS", implode( DIRECTORY_SEPARATOR, array( $project_name, "js" ) ) );		
		}
		
		if( defined( "RELPATH_WEBSITE_MEDIA" ) )
		{
			runkit_constant_redefine( "RELPATH_WEBSITE_MEDIA", implode( DIRECTORY_SEPARATOR, array( $project_name, "media" ) ) );
		}
		else
		{
			define( "RELPATH_WEBSITE_MEDIA", implode( DIRECTORY_SEPARATOR, array( $project_name, "media" ) ) );		
		}
		

		if( defined( "RELPATH_WEBSITE_PAGES" ) )
		{
			runkit_constant_redefine( "RELPATH_WEBSITE_PAGES", implode( DIRECTORY_SEPARATOR, array( $project_name ) ) );		
		}
		else
		{
			define( "RELPATH_WEBSITE_PAGES", implode( DIRECTORY_SEPARATOR, array( $project_name ) ) );		
		}
		WebsiteProjectJSFile::set_directory( RELPATH_WEBSITE_JS );
		WebsiteProjectCSSFile::set_directory( RELPATH_WEBSITE_CSS );
		WebsiteProjectModule::set_directory( RELPATH_MODULES );
		WebsiteProjectTemplate::set_directory( RELPATH_TEMPLATES );		

		
		$website_project_required_directories = array(
			RELPATH_BACKUPS,
			RELPATH_DATABASE,
			RELPATH_MODULES,
			RELPATH_TEMPLATES,
			RELPATH_WEBSITE,
			RELPATH_WEBSITE_CSS,
			RELPATH_WEBSITE_DOWNLOADS,
			RELPATH_WEBSITE_JS,
			RELPATH_WEBSITE_MEDIA
		);
		foreach( $website_project_required_directories as $dir )
		{
			if( !mkdir( makepath( $project_root, $dir ), 0777, TRUE ) )
			{
				// A required website project directory could not be created.
				throw new Exception(
					sprintf(
						ERROR_CREATE_DIRECTORY,
						$dir
					)
				);
			}
		}

		// Try to create the database.  Throw an exception on failure.
		try
		{
			$sqlite3_db = new SQLite3(
				makepath(
					$project_root,
					RELPATH_DATABASE,
					DB_NAME
				)
			);
		}
		catch( Exception $e )
		{
			throw $e;
		}


		/* Create the required database tables.  Throw an exception is a table could
		not be created. */
		foreach(
			array(
				DB_CREATE_TABLE_PAGE,
				DB_CREATE_TABLE_CONTAINER
			)
			as $sql_stmt
		)
		{
			if( !$sqlite3_db->exec( $sql_stmt ) )
			{
				// A table could not be created.  Throw an Exception.
				throw new Exception( ERROR_CREATE_SQLITE_TABLE );
			}
		}
		$sqlite3_db->close();

		// The website project has now been successfully set up.
		return TRUE;
	}


	private static function validate_website_project( $project_name, &$error_message )
	{
		/* This function will be called after the user has decided to open an existing
		website project.  This function makes sure, that the website project is still
		complete in case of required directories and files.  If it is not complete,
		FALSE is returned.  Otherwise, TRUE is returned.
		The error message is set by reference.

		Parameters		$project_name
						string
						The name of the website project.

						&$error_message
						string
						An empty variable that will later contain an error message
						if an error has occured.

		Return values	TRUE or an exception is thrown.

		*/
		// The website project directory has to exist.
		if( !file_exists( makepath( ABSPATH_WEBSITE_PROJECTS, $project_name ) ) )
		{
			/* The website project directory does not exist.  Very unlikely, because
			the entire website project directory is scanned before all available
			website projects are shown in the "open website project dialog". */
			$error_message = sprintf( ERROR_DIRECTORY_DOES_NOT_EXIST, $project_name );
			throw new Exception( $error_message );
		}


		// Redefine constants during runtime if necessary.
		if( defined( "RELPATH_WEBSITE" ) )
		{
			runkit_constant_redefine( "RELPATH_WEBSITE", implode( DIRECTORY_SEPARATOR, array( $project_name ) ) );
		}
		else
		{
			define( "RELPATH_WEBSITE", implode( DIRECTORY_SEPARATOR, array( $project_name ) ) );		
		}
		
		if( defined( "RELPATH_WEBSITE_CSS" ) )
		{
			runkit_constant_redefine( "RELPATH_WEBSITE_CSS", implode( DIRECTORY_SEPARATOR, array( $project_name, "css" ) ) );		
		}
		else
		{
			define( "RELPATH_WEBSITE_CSS", implode( DIRECTORY_SEPARATOR, array( $project_name, "css" ) ) );		
		}
		
		if( defined( "RELPATH_WEBSITE_DOWNLOADS" ) )
		{
			runkit_constant_redefine( "RELPATH_WEBSITE_DOWNLOADS", implode( DIRECTORY_SEPARATOR, array( $project_name, "downloads" ) ) );
		}
		else
		{
			define( "RELPATH_WEBSITE_DOWNLOADS", implode( DIRECTORY_SEPARATOR, array( $project_name, "downloads" )) );		
		}
		
		if( defined( "RELPATH_WEBSITE_JS" ) )
		{
			runkit_constant_redefine( "RELPATH_WEBSITE_JS", implode( DIRECTORY_SEPARATOR, array( $project_name, "js" ) ) );
		}
		else
		{
			define( "RELPATH_WEBSITE_JS", implode( DIRECTORY_SEPARATOR, array( $project_name, "js" ) ) );		
		}
		
		if( defined( "RELPATH_WEBSITE_MEDIA" ) )
		{
			runkit_constant_redefine( "RELPATH_WEBSITE_MEDIA", implode( DIRECTORY_SEPARATOR, array( $project_name, "media" ) ) );
		}
		else
		{
			define( "RELPATH_WEBSITE_MEDIA", implode( DIRECTORY_SEPARATOR, array( $project_name, "media" ) ) );		
		}
		

		if( defined( "RELPATH_WEBSITE_PAGES" ) )
		{
			runkit_constant_redefine( "RELPATH_WEBSITE_PAGES", implode( DIRECTORY_SEPARATOR, array( $project_name ) ) );		
		}
		else
		{
			define( "RELPATH_WEBSITE_PAGES", implode( DIRECTORY_SEPARATOR, array( $project_name ) ) );		
		}
		WebsiteProjectJSFile::set_directory( RELPATH_WEBSITE_JS );
		WebsiteProjectCSSFile::set_directory( RELPATH_WEBSITE_CSS );
		WebsiteProjectModule::set_directory( RELPATH_MODULES );
		WebsiteProjectTemplate::set_directory( RELPATH_TEMPLATES );


		// Check whether all required directories exist.				
		foreach(
			array(
				RELPATH_BACKUPS,
				RELPATH_DATABASE,
				RELPATH_MODULES,
				RELPATH_TEMPLATES,
				RELPATH_WEBSITE,
				RELPATH_WEBSITE_CSS,
				RELPATH_WEBSITE_DOWNLOADS,
				RELPATH_WEBSITE_JS,
				RELPATH_WEBSITE_MEDIA	
			)
			as $dir
		)
		{
			if( !file_exists(
				makepath(
					ABSPATH_WEBSITE_PROJECTS,
					$project_name,
					$dir
				)
			))
			{
				// Error:  A required website project directory does not exist.
				$error_message = sprintf( ERROR_DIRECTORY_DOES_NOT_EXIST, $dir );
				throw new Exception( $error_message );
			}
		}
	
		// Check if the database exists.
		if( !file_exists(
			makepath(
				ABSPATH_WEBSITE_PROJECTS,
				$project_name,
				RELPATH_DATABASE,
				DB_NAME
			)
		))
		{
			// Error:  The database does not exist.
			$error_message = sprintf( ERROR_DATABASE_DOES_NOT_EXIST, DB_NAME );
			throw new Exception( $error_message );
		}
		
	
		// Check if the tables inside the database exist.
		try
		{
			$sqlite3db = new SQLite3(
				makepath(
					ABSPATH_WEBSITE_PROJECTS,
					$project_name,
					RELPATH_DATABASE,
					DB_NAME
				)
			);
		}
		catch( Exception $e )
		{
			throw $e;
		}


		foreach(
			array(
				"page",
				"container"
			)
			as $tbl_name
		)
		{
			$sqlite_result = $sqlite3db->query(
				sprintf(
					"SELECT name FROM sqlite_master
					WHERE type = 'table' AND
					name = '%s'",
					$tbl_name
				)
			);
			if( $sqlite_result->fetchArray() == FALSE )
			{
				// Error:  A required sqlite table does not exist.
				$error_message = sprintf(
					ERROR_DATABASE_TABLE_DOES_NOT_EXIST,
					$tbl_name
				);
				throw new Exception( $error_message );
			}
			$sqlite_result->finalize();
		}
		$sqlite3db->close();

		/* The project directory has been validated successfully.  The project
		can now be opened. */
		return TRUE;
	}


	public function recreate_website_structure()
	{
		$this->fetch_all_pages();
		$this->create_all_pages();
		$this->website_structure = $this->create_entire_website_structure();
	}


	public function fetch_all_pages()
	{
		$this->all_pages = $this->ssq->select(
			"*",
			array( "page" ),
			NULL,
			"ORDER BY sort_id ASC"
		);
	}


	public function create_all_pages()
	{
		$this->website_project_pages = array();
		foreach( $this->all_pages as $key => $value )
		{
			// Fetch containers of that page.
			$containers = $this->fetch_containers( $value["id"] );

			$this->website_project_pages[] = new WebsiteProjectPage(
				$value["id"],
				$value["name"],
				$value["template"],
				$value["parent_id"],
				$value["sort_id"],
				$containers
			);
		}
	}


	public function get( $property )
	{
		return $this->$property;
	}


	public function get_parent_pages()
	{
		$arr = array();
		foreach( $this->website_project_pages as $value )
		{
			$arr[$value->get( "name" )] = $value;
		}
		
		return $arr;
	}


	public function get_child_pages( $parent_id ) // Siblings!
	{
		
		$arr = array();
		foreach( $this->website_project_pages as $value )
		{
			if( $value->get( "parent_id" ) == $parent_id )
			{
				$arr[$value->get( "name" )] = $value;
			}
		}
		return $arr;
		
	}


	public function rename_page( $page_id, $newname )
	{
		$this->ssq->update(
			"page",
			array(
				array( "name", "=", $newname )
			),
			array(
				array( "id", "=", $page_id )
			)
		);
	}


	public function delete_page( $page_id )
	{
		$this->ssq->delete(
			"page",
			array(
				array( "id", "=", $page_id )
			)
		);
	}


	public function page_update_sort_id( $id, $sort_id )
	{
		/* Re-sets the value of the column 'sort_id' for a certain row in the table
		'page'.  On success, TRUE is returned.  On failure, FALSE is returned.

		Parameters		$id
						integer
						The page of which the sort id needs to be changed.

						$sort_id
						id
						Contains the new sort id.

		Return values	TRUE/FALSE
						TRUE is returned on success; FALSE on failure.

		*/

		$this->ssq->update(
			"page",
			array(
				array( "sort_id", "=", $sort_id )
			),
			array(
				array( "id", "=", $id )
			)
		);
	}


	public function rename_template_of_page( $id, $template )
	{
		$this->ssq->update(
			"page",
			array(
				array( "template", "=", $template )
			),
			array(
				array( "id", "=", $id )
			)
		);
	}


	public function page_update_template( $id, $template, $old_template )
	{
		$this->ssq->update(
			"page",
			array(
				array( "template", "=", $template )
			),
			array(
				array( "id", "=", $id )
			)
		);

		$this->page_delete_create_containers( $id, $template, $old_template );
	}


	public function reverse_two_page_items( $item_to_move_up_id,
		$item_to_move_up_sort_id, $item_to_move_down_id, $item_to_move_down_sort_id )
	{
		/* Reverses the order of two items, each of which represents a page.
		On success, TRUE is returned, otherwise FALSE is returned.

		Parameters		$item_to_move_up_id
						id
						The id of the item to move up.					

						$item_to_move_down_id
						id
						The id of the item to move down.

		Return values	TRUE/FALSE
						TRUE is returned on success; FALSE on failure.

		*/
		$this->ssq->update(
			"page",
			array(
				array( "sort_id", "=", $item_to_move_up_sort_id )
			),
			array(
				array( "id", "=", $item_to_move_up_id )
			)
		);

		$this->ssq->update(
			"page",
			array(
				array( "sort_id", "=", $item_to_move_down_sort_id ),
			),
			array(
				array( "id", "=", $item_to_move_down_id )
			)
		);
	}


	function move_page_into_page( $new_sort_id, $id, $new_parent_id )
	{
		/* Re-sets the value of the column "parent_id" for a certain row in the table
		'page'.  The row is identified by the '$id'.  Also resets the "sort_id".
		On success, TRUE is returned.  On failure, FALSE is returned.

		Parameters		$new_sort_id
						integer
						The new sort id for the page.

						$id
						integer
						The id of the page to which to assign the new sort id and
						the new parent id.

						$new_parent_id
						integer

		Return values	TRUE/FALSE
						TRUE is returned on success; FALSE on failure.

		*/
		$this->ssq->update(
			"page",
			array(
				array( "parent_id", "=", $new_parent_id ),
				array( "sort_id", "=", $new_sort_id )
			),
			array(
				array( "id", "=", $id )
			)
		);
	}


	function page_delete_create_containers( $page_id, $template, $old_template = NULL )
	{
		/* Deletes and creates containers.  Containers that exist in both arrays
		are not deleted nor created. */

		if( $old_template == NULL )
		{
			// The template has not been changed, but the containers may have.
			$new_page_containers = $this->template_elements[$template]->get( "page_container_names" );
			$old_page_containers_records = $this->ssq->select(
				array( "name" ),
				array( "container" ),
				array(
					array( "page_id", "=", $page_id )
				)
			);
			$old_page_containers = array();
			foreach( $old_page_containers_records as $opcr )
			{
				$old_page_containers[] = $opcr["name"];
			}
		}
		else
		{
			// The template for the page has been changed.
			$old_page_containers = $this->template_elements[$old_template]->get( "page_container_names" );
			$new_page_containers = $this->template_elements[$template]->get( "page_container_names" );
		}

		$containers_to_delete = array_diff( $old_page_containers, $new_page_containers );
		$containers_to_create = array_diff( $new_page_containers, $old_page_containers );
				
		foreach( $containers_to_delete as $container_name )
		{
			$this->ssq->delete(
				"container",
				array(
					array( "page_id", "=", $page_id ),
					array( "AND" ),
					array( "name", "=", $container_name )
				)
			);
		}

		foreach( $containers_to_create as $container_name )
		{
			$this->ssq->insert(
				"container",
				array( "name", "content", "page_id" ),
				array( $container_name, "", $page_id )
			);
		}
	}


	function generate_pages( $website_structure, $root_dir, $abs_template_path )
	{
		/* Generates the website directory structure and the .HTML pages of course.

		Throws an Exception if an error has occured.

		Parameters		$website_structure
						array
						An array that contains a representation of the structure of
						the website.  Each item is an array that consists of several
						attributes, each of which are defined in the database.
						So, essentially, all items are just a mere representation of
						their respective values on the database table 'page'.

						$root_dir
						string
						The absolute path to the "out/pages" directory of a website
						project.

						$abs_template_path
						string
						The absolute path to the "in/templates" directory of a website
						project.

		Return values	Void

		For a detailed idea of how the function works, please read through the body
		of this function.

		*/
		// Each page in the website structure will be created.
		foreach( $website_structure as $page )
		{
			// Settings values for our global variables to be used within a module.
			global $WP_ENTIRE_WEBSITE_STRUCTURE;
			$WP_ENTIRE_WEBSITE_STRUCTURE = $this->website_structure;

			global $WP_PAGE_NAME;
			$WP_PAGE_NAME = $page["self"]->get( "name" );

			global $WP_PAGE_ID;
			$WP_PAGE_ID = $page["self"]->get( "id" );

			global $WP_PAGE_PARENT_ID;
			$WP_PAGE_PARENT_ID = $page["self"]->get( "parent_id" );

			global $WP_PAGE_SORT_ID;
			$WP_PAGE_SORT_ID = $page["self"]->get( "sort_id" );

      		
			
			// The content of the template which the current page uses.
			$templates = $this->get_elements( WEBSITE_PROJECT_TEMPLATE_FILES );
			foreach( $templates as $tmp )
			{
				if( strcmp( $tmp->get( "name" ), $page["self"]->get( "template" ) ) == 0 )
				{
					$template_content = $tmp->get_content();
					$template_page_containers = $tmp->get( "page_container_names" );
					$template_function_containers = $tmp->get( "function_container_names" );
					break;
				}
			};

			// The containers of the current page.
			$objs_page_containers = $page["self"]->get( "containers" );

			/* Find and execute any functions, that are contained in a page container.
			and save the result. */
			$page_containers = array();
			foreach( $objs_page_containers as $opc )
			{
				$page_containers[$opc->get( "name" )] = find_and_execute_functions( $opc->get( "content" ) );
			}

				
			/* Find and execute any functions, that are contained in the template
			and save the results. */
			$template_content = find_and_execute_functions(
				$template_content
			);
		
			// Replace page containers of the template by their respective values.
			$search_replace = array();
			foreach( $page_containers as $key => $value )
			{
				$search_replace[$key] = $value;
			}
		
			/* Generate the final HTML code with all containers being replaced with
			their respective values. */
			$final_html_code = str_replace(
				array_keys( $search_replace ),
				array_values( $search_replace ),
				$template_content
			);
		
			// Create the directory.
			$res = mkdir(
				makepath(
					$root_dir,
					$page["self"]->get( "name" )
				),
				0777,
				TRUE
			);
			if( $res == FALSE )
			{
				// The directory could not be created.
				throw new Exception(
					sprintf(
						ERROR_CREATE_DIRECTORY,
						$page["self"]["name"]
					)
				);
			}

			// Write the contents into the "index.html" file.	
			$res = file_put_contents(
				makepath(
					$root_dir,
					$page["self"]->get( "name" ),
					"index.html"
				),
				$final_html_code
			);
			if( $res === FALSE )
			{
				// Contents could not be written.
				throw new Exception(
					sprintf(
						ERROR_CREATE_FILE,
						"index.html"
					)
				);
			}
		
			// Move into the deeper branch.
			$this->generate_pages(
				$page["children"],
				makepath( $root_dir, $page["self"]->get( "name" ) ),
				$abs_template_path
			);
		}
	}
}

?>
